﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharedGenomics.Domain
{
    public abstract class DataColumnTypeBase
    {
        #region Private Members

        private Delegate _map;    //Func<S, D>
        private Type _maskedType;
        private string _name;
        private ICollection<object> _values;

        #endregion

        #region Constructors

        public DataColumnTypeBase(string name, bool unique, bool nullable)
        {
            this.Name = name;
            this.Unique = unique;
            this.Nullable = nullable;

            this._values = new HashSet<object>();
            this.Index = -1;
        }

        #endregion

        #region Public Methods

        public abstract object ResolveValue(string input);

        public void SetMap<SourceType, DestinationType>(Func<SourceType, DestinationType> map)
        {
            if (map == null)
            {
                this.ClearMap();
            }
            else
            {
                if (typeof(SourceType) != this.DataType)
                    throw new ArgumentException();

                //update the map, save the current datatype (as it is about to be hidden by the map return type) and update the column type to match the 
                //map output type
                this._map = map;
                this._maskedType = this.DataType;
                this.DataType = typeof(DestinationType);
            }
        }

        public abstract void CreateMap<T>(params KeyValuePair<string, T>[] mappings);

        public void ClearMap()
        {
            //clear the map member and restore the currently-hidden datatype
            this._map = null;
            this.DataType = this._maskedType;
        }

        #endregion

        #region Protected Methods

        protected object ApplyMap(object input)
        {
            if (this._map == null)
                return input;
            else
            {
                return this._map.DynamicInvoke(input);
            }
        }

        protected void AddValue(object value)
        {
            if (value != null)
                this._values.Add(value);
        }

        #endregion

        #region Properties

        public string Name
        {
            get { return this._name; }
            private set
            {
                if (value == null)
                    throw new ArgumentNullException("Name");

                this._name = value;
            }
        }

        public string HeaderText { get; set; }
        public Type DataType { get; protected set; }

        public bool IsCandidateKey
        {
            get { return this.Unique && !this.Nullable; }
        }

        public bool Unique { get; private set; }
        public bool Nullable { get; private set; }

        public int ValueCount
        {
            get { return this._values.Count; }
        }

        public int Index { get; internal set; }

        #endregion
    }
}
